package yycore

import chisel3._
import chisel3.util._
import chisel3.util.experimental.BoringUtils
import common.Constants._
import bus._
import top._

class InstrPackage extends Bundle {
  val instValid = Output(Bool())
  val inst = Output(UInt(32.W))
  val pc = Output(UInt(AddrBits.W))
  val prepcNext = Output(UInt(AddrBits.W))
}

trait ResetVector {
  val resetVector = Settings.getLong("ResetVector")
}

class IFU() extends Module with ResetVector{
  val io = IO(new Bundle() {
    //val flush = Input(Bool())
    val imem = new CoreLinkIO(32)
    val out = Decoupled(new InstrPackage())
    val redirect = Flipped(new RedirectIO)
  })
  io := DontCare

  //**********************************
  // Pipeline State Registers
  val pc = RegInit(resetVector.asUInt(AddrBits.W))

  // *********************************
  val bpu = Module(new BPU())
  val preNextPc = bpu.io.out.target

  // *********************************
  // update pc reg
  val redirectValid = io.redirect.valid
  val if_pc_plus4 = (pc + 4.asUInt(AddrBits.W))
  val if_pc_next = Mux(redirectValid, io.redirect.target, Mux(bpu.io.out.valid, preNextPc, if_pc_plus4))
  val imiss = io.imem.req.valid && !io.imem.resp.fire()

  bpu.io.in.valid := io.imem.req.fire()//!redirectValid
  bpu.io.in.bits.pc := if_pc_next//Mux(io.out.fire(), if_pc_next, if_pre_pc)

  // *********************************
  // update pc reg
  when(redirectValid || io.imem.req.fire()) {
    pc := if_pc_next
  }
  val lastPC = RegEnable(pc, io.imem.req.fire())
  val prepcNext = RegEnable(if_pc_next, io.imem.req.fire())


  // interrupt
  //BoringUtils.addSource(lastPC, "if_pc")

  // *********************************
  // fetch instructions from imem

  io.imem.apply(valid = io.out.ready, addr = pc, wdata = 0.U, size = 2.U, cmd = LinkBusCmd.read)
  io.imem.resp.ready := io.out.ready //|| redirectValid

  io.out.valid := (io.imem.resp.valid && !redirectValid)
  io.out.bits.inst := io.imem.resp.bits.rdata
  io.out.bits.pc := lastPC
  io.out.bits.instValid := true.B
  io.out.bits.prepcNext := prepcNext
}


